package edu.ccut.saturn.component.impl.node;

import java.util.ArrayList;
import java.util.List;

import edu.ccut.saturn.component.ActionViewException;
import edu.ccut.saturn.component.ComponentManager;
import edu.ccut.saturn.component.ILogicContext;
import edu.ccut.saturn.component.IParameterContext;
import edu.ccut.saturn.component.IParameterInfo;
import edu.ccut.saturn.component.SaturnComponentException;
import edu.ccut.saturn.component.SaturnDebugThreadsManager;
import edu.ccut.saturn.component.impl.SaturnActionContext;
import edu.ccut.saturn.component.impl.SaturnParameterInfo;
import edu.ccut.saturn.component.loader.node.NodeInfoLoader;
import edu.ccut.saturn.component.loader.node.ViewNodeInfoLoader;
import edu.ccut.saturn.component.view.JsonView;
import edu.ccut.saturn.component.view.JspView;
import edu.ccut.saturn.component.view.StreamView;
import edu.ccut.saturn.component.view.XmlView;
import edu.ccut.saturn.component.xml.ISaturnElement;
import edu.ccut.saturn.component.xml.SaturnElement;

/**
 * 视图结点的实现类。视图结点被action组件调用，表示action组件将数据做为响应信息返回给用户的过程。
 * <p>
 * 视图结点一共有3种类型：
 * <li>jsp：表示视图结点将采用跳转到jsp页面的方式返回响应给用户。
 * <li>xml：表示视图结点把xml格式描述的数据作为响应返回给当前调用页面。
 * <li>binary：表示视图结点把二进制的数据作为响应返回给当前调用页面。
 * <p>
 * <code>ViewNodeInfo</code>类扩展了<code>AbstractNodeInfo</code>：
 * <li>type：表示视图结点的类型；
 * <li>location：当视图结点的类型是jsp时，这个字段记录了要显示的jsp页面的路径。这个路径是从根路径“/”开始的相对路径。并支持表达式，可以动态生成jsp路径。
 * 当视图结点类型是xml或bianary时，这个字段记录了作为响应的数据在上下文中的位置。
 * 
 * @author 谷钰
 * @author 温建
 * 
 * @version 1.0 2008.8.22
 * 
 * @see AbstractNodeInfo
 * @see ActionViewException
 * @see ILogicContext
 * @see JspView
 * @see XmlView
 * @see StreamView
 * 
 */
public class ViewNodeInfo extends AbstractNodeInfo {

	private final String VIEW_TYPE_JSP = "jsp";

	private final String VIEW_TYPE_XML = "xml";

	private final String VIEW_TYPE_JSON = "json";

	private final String VIEW_TYPE_BINARY = "binary";

	private String location = null;

	private String type = null;

	public ViewNodeInfo(String id, String name, String description,
			String location, String type, String x, String y) {

		super(id, name, description, null, null, x, y, "view");
		this.location = location;
		this.type = type;
	}

	/**
	 * 获得视图结点location属性的值
	 * 
	 * @return
	 */
	public String getLocation() {
		return location;
	}

	/**
	 * 获得视图结点的类型
	 * 
	 * @return
	 */
	public String getType() {
		return type;
	}

	/**
	 * 视图结点的执行逻辑。 首先判断视图结点的类型。jsp类型则先获得jsp的路径，生成JspView对象并抛出<code>ActionViewException</code>异常用于结束action组件的执行。
	 * 如果是xml或binary的视图结点类型，则从上下文中获得需要返回的数据的对象并生成<code>XmlView</code>或<code>StreamView</code>的对象，并抛出<code>ActionViewException</code>异常用于结束action组件的执行。
	 * 
	 * @param logicContext
	 *            逻辑上下文对象，记录结点运行时的上下文信息
	 * @throws Exception
	 */
	@Override
	public void doExecute(ILogicContext logicContext) throws Exception {
		ComponentManager.beforeExecuteComponent(this, this
				.createParameterContext(logicContext));

		// SaturnParameterContext afterParameterContext = null;

		try {
			logger.debug("      node : {'execute : { path : '" + this.location
					+ "', type : '" + this.type + "'}', process : '" + logicContext.getId() + "'}");

			if (VIEW_TYPE_JSP.equals(this.type)) {
				// TODO:using throw ActionViewException is not a good method,
				// can
				// improving this?
				SaturnActionContext actionContext = null;

				if (logicContext instanceof SaturnActionContext) {
					actionContext = (SaturnActionContext) logicContext;
					String pathStr = this.location;

					if (actionContext.isExpression(this.location)) {
						Object path = (String) actionContext
								.getExpression(this.location);

						if (path != null) {
							pathStr = (String) path;

						} else {
							SaturnComponentException e = new SaturnComponentException(
									" found exception when executed View Node "
											+ super.getId()
											+ " the path of page to show is invalid");

							logger
									.error(
											" found exception when executed View Node "
													+ super.getId()
													+ " the path of page to show is invalid",
											e);
							throw e;
						}
					}
					throw new ActionViewException(new JspView(pathStr));
				}

			} else if (VIEW_TYPE_XML.equals(this.type)) {
				Object xmlData = logicContext.getValue(this.location);

				String xml = "";
				if (xmlData != null) {
					xml = xmlData.toString();
				}

				throw new ActionViewException(new XmlView(xml));
			} else if (VIEW_TYPE_JSON.equals(this.type)) {
				Object jsonData = logicContext.getValue(this.location);

				String json = "";
				if (jsonData != null) {
					json = jsonData.toString();
				}
				throw new ActionViewException(new JsonView(json));
			} else if (VIEW_TYPE_BINARY.equals(this.type)) {
				Object binaryData = logicContext.getValue(this.location);

				if (binaryData != null) {
					throw new ActionViewException(new StreamView(
							(byte[]) binaryData));
				}
			}

		} finally {
			ComponentManager.afterExecuteComponent(this, null);
		}
	}

	protected IParameterContext createParameterContext(
			ILogicContext logicContext) throws Exception {

		if (SaturnDebugThreadsManager.getInstance().isStart()) {
			initParameterInfos();
			return logicContext.createParameterContext(getParameterInfos());
		}
		return null;
	}

	private void initParameterInfos() {
		List<IParameterInfo> parameters = new ArrayList<IParameterInfo>();

		SaturnParameterInfo typeParameterInfo = new SaturnParameterInfo();
		typeParameterInfo.setClassType(String.class);
		typeParameterInfo.setName("view type");
		typeParameterInfo.setId("1");
		typeParameterInfo.setValueKey("'" + this.type + "'");
		typeParameterInfo.setScope(IParameterInfo.SCOPE_LOGIC);
		typeParameterInfo.setPassingType(IParameterInfo.PASSING_IN);
		parameters.add(typeParameterInfo);

		SaturnParameterInfo valueParameterInfo = new SaturnParameterInfo();
		valueParameterInfo.setClassType(String.class);
		valueParameterInfo.setName("value");
		valueParameterInfo.setId("2");

		if (VIEW_TYPE_JSP.equals(this.type)) {
			valueParameterInfo.setValueKey("'" + this.location + "'");

		} else {
			valueParameterInfo.setValueKey(this.location);
		}
		valueParameterInfo.setScope(IParameterInfo.SCOPE_LOGIC);
		valueParameterInfo.setPassingType(IParameterInfo.PASSING_IN);
		parameters.add(valueParameterInfo);

		SaturnParameterInfo descriptionParameterInfo = new SaturnParameterInfo();
		descriptionParameterInfo.setClassType(String.class);
		descriptionParameterInfo.setName("description");
		descriptionParameterInfo.setId("3");
		descriptionParameterInfo.setValueKey("'" + this.getDescription() + "'");
		descriptionParameterInfo.setScope(IParameterInfo.SCOPE_LOGIC);
		descriptionParameterInfo.setPassingType(IParameterInfo.PASSING_IN);
		parameters.add(descriptionParameterInfo);
		super.setParameterInfos(parameters);
	}

	public ISaturnElement toSaturnElement() {
		ISaturnElement viewElement = new SaturnElement(
				ViewNodeInfoLoader.KEY_NODETYPE_VIEW);

		viewElement.addAttribute(NodeInfoLoader.KEY_NODE_ID, this.getId());

		viewElement
				.addAttribute(NodeInfoLoader.KEY_NODE_NEXT, this.getNextId());

		viewElement.addAttribute(NodeInfoLoader.KEY_NODE_X, this.getX());

		viewElement.addAttribute(NodeInfoLoader.KEY_NODE_Y, this.getY());

		ISaturnElement nameElement = new SaturnElement(
				NodeInfoLoader.KEY_NODE_NAME, this.getName());

		ISaturnElement descriptionElement = new SaturnElement(
				NodeInfoLoader.KEY_NODE_DESCRIPTION, this.getDescription());

		ISaturnElement typeElement = new SaturnElement(
				ViewNodeInfoLoader.KEY_NODE_TYPE, this.type);

		ISaturnElement locationElement = new SaturnElement(
				ViewNodeInfoLoader.KEY_NODE_LOCATION, this.location);

		viewElement.addSubElement(nameElement);
		viewElement.addSubElement(descriptionElement);
		viewElement.addSubElement(typeElement);
		viewElement.addSubElement(locationElement);
		return viewElement;
	}

}
